package satellite

import (
	"bufio"
	"io"
	"net"
	"sync"

	"gddgame.cc/galaxy/utils/def"
)

type TransportType = string

const (
	DefaultTransport TransportType = "tcp"
	SpeedTransport   TransportType = "udp"
	SafeTransport    TransportType = "ws"
)

var (
	connPool = sync.Pool{}
)

func init() {
	connPool.New = func() interface{} {
		return &baseConn{
			mutex: sync.Mutex{},
		}
	}
}

type Transport interface {
	Type() TransportType
	Schema() string
	SetLogger(logger def.Logger)
	Listen() (<-chan Conn, error)
	Dial() (Conn, error)
	Close() error
	Connected() bool
}

type Conn interface {
	net.Conn
	Transport() Transport
	Reader() (io.Reader, error)
	StartWrite() (io.Writer, error)
	EndWrite()
	//ReadData(header []byte) (io.Reader, error)
	//WriteData(header []byte, buffer io.WriterTo) error
	Connected() bool
}

type DialHook interface {
	OnConnect(dial Dial)
	OnDisconnect(dial Dial)
	OnError(dial Dial, err error)
}

type Dial interface {
	Conn
	Error(err error)
	Hook(hook DialHook)
	Reconnect() <-chan struct{}
	Opened() bool
}

type baseConn struct {
	net.Conn
	mutex     sync.Mutex
	reader    *bufio.Reader
	connected bool
	transport Transport
}

func NewBaseConn(conn net.Conn, transport Transport) *baseConn {
	bc := connPool.Get().(*baseConn)
	bc.Conn = conn
	if bc.reader == nil {
		bc.reader = bufio.NewReader(conn)
	} else {
		bc.reader.Reset(conn)
	}

	bc.connected = true
	bc.transport = transport
	return bc
}

func (c *baseConn) Transport() Transport {
	return c.transport
}

func (c *baseConn) Close() error {
	c.connected = false
	err := c.Conn.Close()
	c.Conn = nil
	connPool.Put(c)
	return err
}
func (c *baseConn) Reader() (io.Reader, error) {
	return c.reader, nil
}

func (c *baseConn) StartWrite() (io.Writer, error) {
	c.mutex.Lock()
	return c.Conn, nil
}

func (c *baseConn) EndWrite() {
	c.mutex.Unlock()
}
func (c *baseConn) ReadData(header []byte) (io.Reader, error) {
	var err error
	if header == nil {
		return c.reader, nil
	}
	_, err = c.reader.Read(header)
	if err != nil {
		if err == io.EOF {
			c.connected = false
		}
		return nil, err
	}
	return c.reader, nil
}

func (c *baseConn) WriteData(header []byte, buffer io.WriterTo) error {
	c.mutex.Lock()
	var count int
	var err error
	count, err = c.Conn.Write(header)
	//fmt.Println(count, err)
	if err != nil || count == 0 {
		if err == io.EOF {
			c.connected = false
		}
		goto Return
	}
	_, err = buffer.WriteTo(c.Conn)
	if err != nil {
		if err == io.EOF {
			c.connected = false
		}
		goto Return
	}
	//fmt.Println(count, err)
Return:
	c.mutex.Unlock()
	return err
}

func (c *baseConn) Connected() bool {
	return c.connected
}
